#ifndef _OS_LINUX_
#include "StdAfx.h"
#endif
#include "../include/FMITSocket.h"
#include "string.h"
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <iostream>

namespace Polaris_CommonLib {

#ifdef _OS_WIN32_
WSADATA g_wsaData;

bool CFMITSocket::m_IsWin32SocketInit = false;
#else
#define SOCKET_ERROR -1
#endif

CFMITSocket::CFMITSocket() :
  m_sock ( -1 )
{

#ifdef _OS_WIN32_
	if( m_IsWin32SocketInit == false )
	{
		int win32InitResult = WSAStartup( MAKEWORD(2,2), &g_wsaData);
		if( win32InitResult == 0 )
		{
			// ERROR
		}
	}
#endif

  memset ( &m_addr,
	   0,
	   sizeof ( m_addr ) );

}

CFMITSocket::~CFMITSocket()
{
  if ( is_valid() )
  {
#ifdef _OS_LINUX_
    ::close ( m_sock );
#else
	  ::closesocket( m_sock );
#endif
  }
}

bool CFMITSocket::create()
{
  m_sock = socket ( AF_INET,
		    SOCK_STREAM,
		    0 );

  if ( ! is_valid() )
    return false;


  // TIME_WAIT - argh
  int on = 1;
  if ( setsockopt ( m_sock, SOL_SOCKET, SO_REUSEADDR, ( const char* ) &on, sizeof ( on ) ) == -1 )
    return false;


  return true;

}



bool CFMITSocket::bind ( const int port )
{

  if ( ! is_valid() )
    {
      return false;
    }



  m_addr.sin_family = AF_INET;
  m_addr.sin_addr.s_addr = INADDR_ANY;
  m_addr.sin_port = htons ( port );

  int bind_return = ::bind ( m_sock,
			     ( struct sockaddr * ) &m_addr,
			     sizeof ( m_addr ) );


  if ( bind_return == -1 )
    {
      return false;
    }

  return true;
}


bool CFMITSocket::listen() const
{
  if ( ! is_valid() )
    {
      return false;
    }

  int listen_return = ::listen ( m_sock, MAXCONNECTIONS );


  if ( listen_return == -1 )
    {
      return false;
    }

  return true;
}


bool CFMITSocket::accept ( CFMITSocket& new_socket ) const
{
  int addr_length = sizeof ( m_addr );
  new_socket.m_sock = ::accept ( m_sock, ( sockaddr * ) &m_addr, ( socklen_t * ) &addr_length );

  if ( new_socket.m_sock <= 0 )
    return false;
  else
    return true;
}


bool CFMITSocket::send ( const std::string s ) const
{
  int status = ::send ( m_sock, s.c_str(), s.size(),
#ifdef _OS_LINUX_
	  MSG_NOSIGNAL
#else
	  0
#endif
	  );
  if ( status == -1 )
    {
      return false;
    }
  else
    {
      return true;
    }
}


int CFMITSocket::recv ( std::string& s ) const
{
  char buf [ MAXRECV + 1 ];

  s = "";

  memset ( buf, 0, MAXRECV + 1 );

  int status = ::recv ( m_sock, buf, MAXRECV, 0 );

  if ( status == -1 )
    {
      std::cout << "status == -1   errno == " << errno << "  in Socket::recv\n";
      return 0;
    }
  else if ( status == 0 )
    {
      return 0;
    }
  else
    {
      s = buf;
      return status;
    }
}

int CFMITSocket::recv ( std::string& a_data, int timeout_ms ) const
{
	fd_set readResult, writeResult,exceptResult;
	FD_ZERO( &readResult );
	FD_ZERO( &writeResult );
	FD_ZERO( &exceptResult );

	FD_SET( this->m_sock, &readResult );
	//FD_SET( this->m_sock, &writeResult );
	//FD_SET( this->m_sock, &exceptResult );

#if _OS_LINUX_
    timeval selecttime;
#else
	TIMEVAL selecttime;
#endif
	selecttime.tv_usec = timeout_ms;

	if( ::select( this->m_sock + 1, &readResult, &writeResult, &exceptResult,
		timeout_ms >= 0 ? &selecttime : NULL ) > 0 )
	{
		if( FD_ISSET( this->m_sock, &readResult ) )
			return recv( a_data );
		else
			return SOCKET_ERROR;
	}
	else
		return 0;
}

int CFMITSocket::recv( char *a_pBuf, int a_BufSize, int timeout_ms ) const
{
	fd_set readResult, writeResult,exceptResult;
	FD_ZERO( &readResult );
	FD_ZERO( &writeResult );
	FD_ZERO( &exceptResult );

	FD_SET( this->m_sock, &readResult );
	//FD_SET( this->m_sock, &writeResult );
	//FD_SET( this->m_sock, &exceptResult );

#if _OS_LINUX_
    timeval selecttime;
#else
	TIMEVAL selecttime;
#endif
	selecttime.tv_usec = timeout_ms;

	//if( ::select( 1, &readResult, &writeResult, &exceptResult,
    if( ::select( this->m_sock + 1, &readResult, NULL, NULL,
		timeout_ms >= 0 ? &selecttime : NULL ) > 0 )
	{
		if( FD_ISSET( this->m_sock, &readResult ) )
		{
			memset ( a_pBuf, 0, a_BufSize );
			int status = ::recv ( m_sock, a_pBuf, a_BufSize, 0 );
			// if it is readable and no data is received, then the client socket is closed
			return status > 0 ? status : -1;
		}
		else
			return SOCKET_ERROR;
	}
	else
		return 0;
}

bool CFMITSocket::send( char *a_pBuf, int a_BufSize ) const
{
	int status = ::send ( m_sock, a_pBuf, a_BufSize,
#ifdef _OS_LINUX_
	  MSG_NOSIGNAL
#else
	  0
#endif
	  );
  if ( status == -1 )
    {
      return false;
    }
  else
    {
      return true;
    }
}


bool CFMITSocket::connect ( const std::string host, const int port )
{
  if ( ! is_valid() ) return false;

  m_addr.sin_family = AF_INET;
  m_addr.sin_port = htons ( port );

#ifdef _OS_LINUX_
  int status = inet_pton ( AF_INET, host.c_str(), &m_addr.sin_addr );
#else
  int status = 0;
  m_addr.sin_addr.S_un.S_addr = inet_addr( host.c_str() );
#endif

  if ( errno == EAFNOSUPPORT ) return false;

  status = ::connect ( m_sock, ( sockaddr * ) &m_addr, sizeof ( m_addr ) );

  if ( status == 0 )
    return true;
  else
    return false;
}

void CFMITSocket::set_non_blocking ( const bool b )
{
#ifdef _OS_LINUX_
  int opts;

  opts = fcntl ( m_sock,
		 F_GETFL );

  if ( opts < 0 )
    {
      return;
    }

  if ( b )
    opts = ( opts | O_NONBLOCK );
  else
    opts = ( opts & ~O_NONBLOCK );

  fcntl ( m_sock,
	  F_SETFL,opts );

#else // _OS_WIN32_
	int result = ::ioctlsocket( m_sock, FIONBIO,
		(u_long*)(b == true ? 0 : 1) );
#endif
}

} // End namespace
